package com.mf.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mf.basic.BasicDefaultKey;
import com.mf.entity.bo.BasicResourceFileBo;
import com.mf.entity.request.upload.FileFindPageListReq;
import com.mf.entity.request.upload.FileInitSliceDataReq;
import com.mf.entity.request.upload.FileUploadFileReq;
import com.mf.entity.vo.BasicDataDictionaryVo;
import com.mf.entity.vo.BasicFileVo;
import com.mf.enums.EnumBasicResultCode;
import com.mf.enums.EnumViewContentType;
import com.mf.mapper.BasicResourceFileMapper;
import com.mf.service.IBasicDataDictionaryService;
import com.mf.service.upload.IBasicResourceFileService;
import com.mf.utils.MinIOUtils;
import io.github.heollhai.common.basic.BasicKey;
import io.github.heollhai.common.config.exceptions.GraceException;
import io.github.heollhai.common.config.reids.RedisOperator;
import io.github.heollhai.common.config.security.utils.SecurityUtils;
import io.github.heollhai.common.entity.security.dto.UserByRoleDto;
import io.github.heollhai.common.utils.CommonUtils;
import io.github.heollhai.common.utils.MybatisPlusUtils;
import io.github.heollhai.common.utils.VerifyUtils;
import io.github.heollhai.common.utils.enums.EnumUtils;
import io.minio.StatObjectResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 文件表 服务实现类
 *
 * @author mf
 * @since 2024-11-20
 */
@Service
@Slf4j
public class BasicResourceFileServiceImpl extends ServiceImpl<BasicResourceFileMapper, BasicResourceFileBo> implements IBasicResourceFileService {
    @Resource
    private BasicResourceFileMapper basicResourceFileMapper;

    @Resource
    private RedisOperator redisDao;
    @Resource
    private MinIOUtils minIOUtils;

    @Resource
    private IBasicDataDictionaryService basicDataDictionaryService;

    @Override
    public BasicResourceFileBo selectById(Integer id) {
        return basicResourceFileMapper.selectById(id);
    }

    /**
     * 获取redis图片访问key
     */
    private String getImageRedisKey(Integer id) {
        UserByRoleDto userByRoleDto = SecurityUtils.getUserByRoleDto();
        return CommonUtils.MD5(BasicDefaultKey.IMAGE_TOKEN + id + userByRoleDto.getUserBo().getName());
    }

    /**
     * 批量获取图片权限
     */
    @Override
    public List<String> bachGetFileToken(String ids) {
        String[] split = ids.split(",");
        List<String> tokens = new ArrayList<>();
        List<BasicResourceFileBo> basicResourceFileBos = basicResourceFileMapper.selectBatchIds(List.of(split));
        for (BasicResourceFileBo basicResourceFileBo : basicResourceFileBos) {
            tokens.add(getFileToken(basicResourceFileBo.getId()));
        }
        return tokens;
    }

    /**
     * 获取预览图片权限
     * 根据图片id获取图片访问token
     * 对待token访问2分钟内不限制权限
     */
    @Override
    public String getFileToken(Integer id) {
        BasicResourceFileBo basicResourceFileBo = basicResourceFileMapper.selectById(id);
        if (VerifyUtils.isEmpty(basicResourceFileBo)) {
            GraceException.display(EnumBasicResultCode.FILE_NOT_EXIST);
        }
        checkPermission(basicResourceFileBo.getFileType());
        String redisKey = getImageRedisKey(id);
        redisDao.set(redisKey, BasicKey.DEFAULT_VALUE, BasicDefaultKey.IMAGE_TOKEN_EXPIRE_TIME);
        return redisKey;
    }



    /**
     * 文件预览
     */
    @Override
    public ResponseEntity<InputStreamResource> previewById(Integer id, String fileToken) {

        String s = redisDao.get(fileToken);
        if (VerifyUtils.isEmpty(s)) {
            GraceException.display(EnumBasicResultCode.FILE_TOKEN_EXPIRE);
        }

        BasicResourceFileBo basicResourceFileBo = basicResourceFileMapper.selectById(id);
        if (VerifyUtils.isEmpty(basicResourceFileBo)) {
            GraceException.display(EnumBasicResultCode.FILE_NOT_EXIST);
        }
        return getMinioFile(basicResourceFileBo);
    }

    @Override
    public ResponseEntity<InputStreamResource> getMinioFile(BasicResourceFileBo basicResourceFileBo) {
        // 获取minio文件名称
        final String objectName = minIOUtils.getObjectName(basicResourceFileBo.getFileArea(),
                basicResourceFileBo.getFileBindId(), basicResourceFileBo.getFileType(),
                basicResourceFileBo.getMinioMd5()) + minIOUtils.getFileSuffix(basicResourceFileBo.getOriginalFileName());

        // 获取对象内容
        InputStream imgStream = null;

        try {
            imgStream = minIOUtils.getFileStream(basicResourceFileBo.getMinioBucket(), objectName);

            // 获取文件的实际大小
            StatObjectResponse statObjectResponse = minIOUtils.statObject(basicResourceFileBo.getMinioBucket(),
                    objectName);
            // 确定正确的 MIME 类型
            String contentType = EnumViewContentType.getContentType(objectName);

            // 创建 InputStreamResource
            InputStreamResource resource = new InputStreamResource(imgStream);

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType(contentType));
            headers.setContentLength(statObjectResponse.size());

            return ResponseEntity.ok().headers(headers).body(resource);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 查询文件列表
     */
    @Override
    public Page<BasicFileVo> findPageList(FileFindPageListReq fileFindPageListReq) {
        checkPermission(fileFindPageListReq.getFileType());
        Page<BasicResourceFileBo> basicResourceFileBoPage = new Page<>(fileFindPageListReq.getPageNum(),
                fileFindPageListReq.getPageSize());
        LambdaQueryWrapper<BasicResourceFileBo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicResourceFileBo::getFileType, fileFindPageListReq.getFileType())
                .eq(BasicResourceFileBo::getFileBindId, fileFindPageListReq.getFileBindId())
                .orderByDesc(BasicResourceFileBo::getCreatedTime)
        ;
        if (VerifyUtils.isNotEmpty(fileFindPageListReq.getKey())) {
            wrapper.like(BasicResourceFileBo::getOriginalFileName, fileFindPageListReq.getKey()).or().like(BasicResourceFileBo::getRemark, fileFindPageListReq.getKey());
        }
        Page<BasicResourceFileBo> basicResourceFileBoPage1 =
                basicResourceFileMapper.selectPage(basicResourceFileBoPage, wrapper);
        return MybatisPlusUtils.changePageDataClass(basicResourceFileBoPage1, BasicFileVo.class);
    }

    @Override
    public Boolean insertFile(BasicResourceFileBo resourceFile) {
        try {
            basicResourceFileMapper.insert(resourceFile);
            return true;
        } catch (Exception e) {
            log.error("保存tResourceFile异常|参数:{}", resourceFile, e);
            return false;
        }
    }

    /**
     * 判断文件是否已存在
     *
     * @return -2 未存在，-1 已存在，其他当前需要上传文件的下标
     */
    @Override
    public Integer checkIsExistFile(FileInitSliceDataReq fileInitSliceDataReq) {
        checkPermission(fileInitSliceDataReq.getFileType());
        // 完成上传标识
        final LambdaQueryWrapper<BasicResourceFileBo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicResourceFileBo::getMinioMd5, fileInitSliceDataReq.getMd5())
                .eq(BasicResourceFileBo::getFileType, fileInitSliceDataReq.getFileType())
                .eq(BasicResourceFileBo::getFileArea, fileInitSliceDataReq.getFileArea())
                .eq(BasicResourceFileBo::getFileBindId, fileInitSliceDataReq.getFileBindId())
                .eq(BasicResourceFileBo::getOriginalFileName, fileInitSliceDataReq.getOriginalFileName())
        ;
        final BasicResourceFileBo basicResourceFileBo = basicResourceFileMapper.selectOne(wrapper);


        // 有文件，且未上传完成
        if (VerifyUtils.isNotEmpty(basicResourceFileBo)) {
            // 已上传完成
            if (basicResourceFileBo.getIsUploaded().equals(1)) {
                return -1;
            }
            // 不分片 0:未完成上传那么重新上传一次 -1:文件已存在
            if (fileInitSliceDataReq.getIsSlice() == 0) {
                return basicResourceFileBo.getIsUploaded() == 0 ? 0 : -1;
            }

            // 文件全名
            String fullMd5Name = minIOUtils.getObjectName(fileInitSliceDataReq.getFileArea(),
                    fileInitSliceDataReq.getFileBindId(), fileInitSliceDataReq.getFileType(),
                    (fileInitSliceDataReq.getMd5() + fileInitSliceDataReq.getFileSuffix()));
            final Object i = redisDao.indexValue(fullMd5Name, 0);
            if (VerifyUtils.isEmpty(i)) {
                GraceException.display(EnumBasicResultCode.UPLOAD_INDEX_UNMATCHED);
            }
            // 获取当前需要上传的文件下标
            try {
                return Integer.parseInt(i.toString());
            } catch (NumberFormatException e) {
                log.error("无法将 Redis 值转换为 Integer: {}", i, e);
                GraceException.display(EnumBasicResultCode.UPLOAD_INDEX_UNMATCHED);
            }
        }


        return -2;
    }

    /**
     * 根据数据字典类型验证是否有查看|预览权限
     */
    @Override
    public void checkPermission(String type) {
        String[] split = type.split("&");
        Map<String, List<BasicDataDictionaryVo>> listMap = basicDataDictionaryService.findByPidCode(split[1], true);

        if (VerifyUtils.isEmpty(listMap)) {
            GraceException.display("没有该文件访问权限");
        }

        List<BasicDataDictionaryVo> dictionaryVos = listMap.get(split[1]);

        if (VerifyUtils.isEmpty(dictionaryVos)) {
            GraceException.display("没有该文件访问权限");
        }

        for (BasicDataDictionaryVo dictionaryVo : dictionaryVos) {
            if (dictionaryVo.getValue().equals(split[0])) {
                return;
            }
        }
        GraceException.display("没有该文件访问权限");
    }

    /**
     * 更新上传文件状态
     */
    @Override
    public Boolean updateFileUploadState(FileUploadFileReq fileUploadFileReq) {
        try {
            // 设置需要更改的参数

            final LambdaQueryWrapper<BasicResourceFileBo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(BasicResourceFileBo::getMinioMd5, fileUploadFileReq.getMd5()).eq(BasicResourceFileBo::getFileType, fileUploadFileReq.getFileType()).eq(BasicResourceFileBo::getFileArea, fileUploadFileReq.getFileArea()).eq(BasicResourceFileBo::getFileBindId, fileUploadFileReq.getFileBindId());

            BasicResourceFileBo basicResourceFileBo = BasicResourceFileBo.builder().isUploaded(1).build();

            basicResourceFileMapper.update(basicResourceFileBo, wrapper);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
